<?php

namespace api\pay\driver;

use api\pay\exception\PayException;
use EasyWeChat\Factory;
use EasyWeChat\Kernel\Support\XML;
use EasyWeChat\Payment\Kernel\Exceptions\InvalidSignException;

class Wechat
{
    // 微信配置
    public $config = [
        'debug' => false,
        'http' => [
            'timeout' => 5.0, // 超时时间（秒）
            'verify' => __DIR__ . '/cacert.pem'
        ]
    ];

    public function __construct($config)
    {
        $this->config = array_merge($this->config, $config);
    }

    /**
     * 回调验证
     *
     * @return array|mixed
     * @throws PayException
     */
    public function notifyVerify($data = '')
    {
        $wechatServer = Factory::payment($this->config);

        try {
            $message = XML::parse(strval($data));
            if (!is_array($message) || empty($message)) {
                throw new PayException('Invalid request XML.', 400);
            }
            $sign = $message['sign'];
            unset($message['sign']);

            if (\EasyWeChat\Kernel\Support\generate_sign($message, $wechatServer->getKey()) !== $sign) {
                throw new PayException('签名错误', 400);
            }
            $data = $message;
            $data['trade_no'] = $data['transaction_id'];

            return $data;
        } catch (\Exception $e) {
            exception_log('微信支付订单通知失败', $e);
            throw new PayException('数据检测失败');
        }
    }

    /**
     * 创建js支付
     *
     * @param string $openId 微信id
     * @param array $data 参数
     * @return array
     * @throws PayException
     */
    public function jsPay($openId, $data)
    {
        $wechatServer = Factory::payment($this->config);
        $options = [
            'body' => $data['subject'],
            'out_trade_no' => $data['order_sn'],
            'total_fee' => $data['amount'] * 100,
            'trade_type' => 'JSAPI',
            'notify_url' => $this->config['notify_url'],
            'openid' => $openId
        ];

        try {
            // 生成预支付码
            $result = $wechatServer->order->unify($options);
            if ($result['return_code'] !== 'SUCCESS' || $result['result_code'] !== 'SUCCESS') {
                throw new PayException('订单生成失败: ' . ($result['return_msg'] ?? '') . ' ' . ($result['err_code_des'] ?? ''));
            }

            // 创建JSAPI参数签名
            return (new \EasyWeChat\Payment\Jssdk\Client($wechatServer))->bridgeConfig($result['prepay_id'], false);
        } catch (\Exception $e) {
            exception_log('微信支付订单生成失败 js', $e);
            throw new PayException('订单生成失败');
        }
    }

    /**
     * 扫码支付
     *
     * @param $data
     */
    public function qrcodePay($data)
    {
        $wechatServer = Factory::payment($this->config);
        $options = [
            'body' => $data['subject'],
            'out_trade_no' => $data['order_sn'],
            'total_fee' => $data['amount'] * 100,
            'trade_type' => 'NATIVE',
            'notify_url' => $this->config['notify_url']
        ];
        try {
            // 生成预支付码
            $result = $wechatServer->order->unify($options);
            if ($result['return_code'] !== 'SUCCESS' || $result['result_code'] !== 'SUCCESS') {
                throw new PayException('订单生成失败: ' . ($result['return_msg'] ?? '') . ' ' . ($result['err_code_des'] ?? ''));
            }

            return $result;
        } catch (\Exception $e) {
            exception_log('微信支付订单生成失败 qrcode', $e);
            throw new PayException('订单生成失败');
        }

    }

    /**
     * 创建手机支付
     *
     * @param array $data 参数
     * @return array
     * @throws PayException
     */
    public function wapPay($data)
    {
        $wechatServer = Factory::payment($this->config);
        $options = [
            'body' => $data['subject'],
            'out_trade_no' => $data['order_sn'],
            'total_fee' => $data['amount'] * 100,
            'trade_type' => 'MWEB',
            'notify_url' => $this->config['notify_url']
        ];
        try {
            // 生成预支付码
            $result = $wechatServer->order->unify($options);
            if ($result['return_code'] !== 'SUCCESS' || $result['result_code'] !== 'SUCCESS') {
                throw new PayException('订单生成失败: ' . ($result['return_msg'] ?? '') . ' ' . ($result['err_code_des'] ?? ''));
            }

            $url = $result['mweb_url'] . '&redirect_url=' . urlencode($this->config['return_url'] ?? '');
            return '<script>window.location.href="' . $url . '";</script>';
        } catch (\Exception $e) {
            exception_log('微信支付订单生成失败 wap', $e);
            throw new PayException('订单生成失败');
        }
    }

    /**
     * 创建app支付
     *
     * @param array $data 参数
     * @return array
     * @throws PayException
     */
    public function appPay($data)
    {
        $wechatServer = Factory::payment($this->config);
        $options = [
            'body' => $data['subject'],
            'out_trade_no' => $data['order_sn'],
            'total_fee' => $data['amount'] * 100,
            'trade_type' => 'APP',
            'notify_url' => $this->config['notify_url']
        ];

        try {
            // 生成预支付码
            $result = $wechatServer->order->unify($options);
            if ($result['return_code'] !== 'SUCCESS' || $result['result_code'] !== 'SUCCESS') {
                throw new PayException('订单生成失败: ' . ($result['return_msg'] ?? '') . ' ' . ($result['err_code_des'] ?? ''));
            }

            // 创建APP参数签名
            return (new \EasyWeChat\Payment\Jssdk\Client($wechatServer))->appConfig($result['prepay_id']);
        } catch (\Exception $e) {
            exception_log('微信支付订单生成失败 app', $e);
            throw new PayException('订单生成失败');
        }
    }

    /**
     * 关闭订单
     *
     * @param $orderSn
     * @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string|void
     */
    public function closeOrder($orderSn)
    {
        try {
            $wechatServer = Factory::payment($this->config);
            $result = $wechatServer->order->close($orderSn);
            if ($result['return_code'] !== 'SUCCESS') {
                throw new PayException('订单关闭失败: ' . $result['return_msg']);
            }

            return $result;
        } catch (\Exception $e) {
            exception_log('微信订单关闭失败', $e);
        }
    }

    /**
     * 根据微信订单号退款
     *
     * @param string $transactionId 微信交易单号
     * @param string $outRefundNo 退款单号
     * @param float $totalFee 订单总费用
     * @param float $refundFee 退款费用
     * @param string $refundDesc 退款说明
     * @return array|\EasyWeChat\Kernel\Support\Collection|object|\Psr\Http\Message\ResponseInterface|string|void
     */
    public function refundByTransactionId($transactionId, $outRefundNo, $totalFee, $refundFee, $refundDesc = '')
    {
        try {
            $wechatServer = Factory::payment($this->config);
            $result = $wechatServer->refund->byTransactionId($transactionId, $outRefundNo, $totalFee * 100, $refundFee * 100, [
                'refund_desc' => $refundDesc
            ]);


            return $result;
        } catch (\Exception $e) {
            exception_log('微信退款失败', $e);
        }
    }
}